;;;; settings.lisp
;;;;
;;;; This file contains variables that can be used to control how
;;;; Coalton works.

(defpackage #:coalton-impl/settings
  (:use #:cl)
  (:export
   #:coalton-release-p                  ; FUNCTION
   #:*coalton-disable-specialization*   ; VARIABLE
   #:*coalton-heuristic-inlining*       ; VARIABLE
   #:*coalton-print-unicode*            ; VARIABLE
   #:*coalton-type-printing-mode*       ; VARIABLE
   #:*emit-type-annotations*            ; VARIABLE
   #:*coalton-optimize*                 ; VARIABLE
   #:*coalton-optimize-library*         ; VARIABLE
   #:*compile-print-types*              ; VARIABLE
   #:*print-specialization-occurrences* ; VARIABLE
   #:*print-inlining-occurrences*       ; VARIABLE
   #:*print-optimization-passes*        ; VARIABLE
   #:*auto-continue-redefinition*       ; VARIABLE
   ))

(in-package #:coalton-impl/settings)

;;; Configuration reaches the Coalton compiler in 3 ways.
;;;
;;;    1. When Coalton is compiled, several environment variables are
;;;    inquired. These are
;;;
;;;        - COALTON_ENV: Whether to be in development or release mode.
;;;
;;;        - COALTON_DISABLE_SPECIALIZATION: Whether to disable
;;;          function specialization.
;;;
;;;        - COALTON_HEURISTIC_INLINING: Whether to enable automatic
;;;          inlining.
;;;
;;;    2. When Coalton is compiled, configuration on properties of the
;;;    :COALTON-CONFIG symbol are inquired. These are secondary to the
;;;    environment variables.
;;;
;;;    3. When the Coalton compiler is run, several special variables
;;;    control how the code is compiled. The values of the special
;;;    variables are affected by the aforementioned configuration.

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar *config-keys*
    '(:compiler-mode                    ; [string] compiler mode
      :print-unicode                    ; [boolean] print unicode?
      :type-printing-mode               ; [member :types :aliases :types-and-aliases] type printing mode
      :perform-specialization           ; [boolean] use specializations?
      :perform-inlining                 ; [boolean] automatic inlining?
      :emit-type-annotations            ; [boolean] emit type annotations?
      :print-types                      ; [boolean] print types when compiling?
      :print-rewrites                   ; [boolean] print rewriting that occurs (specialization/inlining)?
      :auto-continue-redefinition       ; [boolean] auto continue incompatible redefinitions?
      )
    "Valid configuration keys that can be (SETF GET) on the user configuration variable :COALTON-CONFIG.")

  (defun config (key &key (default nil defaultp))
    "Get the user configuration associated with the key KEY. If it's not
found, return DEFAULT instead."
    (assert defaultp () "A default must be supplied to CONFIG.")
    (cond
      ((member key *config-keys*)
       (get ':coalton-config key default))
      (t
       (warn "Unknown Coalton configuration key: ~A" key)
       default))))


;;; Compiler and runtime configuration

(declaim (type boolean *coalton-print-unicode*))
(defvar *coalton-print-unicode* (config ':print-unicode :default t)
  "Whether to print coalton info using unicode symbols")

(declaim (type (member :types :aliases :types-and-aliases) *coalton-type-printing-mode*))
(defvar *coalton-type-printing-mode* (config ':type-printing-mode :default :types)
  "Whether to display types, aliases, or both, when displaying the type associated with a symbol. 
Must be one of (:TYPES :ALIASES :TYPES-AND-ALIASES).")

(defun coalton-release-p ()
  "Determines how redefinable code generated by Coalton is.

In development mode (i.e. (not (coalton-release-p))), Coalton turns all declare-type forms into CLOS objects
to support a repl based workflow.

In release mode Coalton compiles declare-type forms into frozen structs or even more optimal layouts which may
not support redefinition.

Development mode is the default.

Enable release mode either by setting the UNIX environment variable COALTON_ENV to \"release\", by

    (setf (get ':coalton-config ':compiler-mode) \"release\")

or by pushing `:coalton-release' into `*features*'. Any of these must be done before loading Coalton."
  (uiop:featurep ':coalton-release))

(when (or (string-equal (uiop:getenv "COALTON_ENV")           "release")
          (string-equal (config ':compiler-mode :default nil) "release"))
  (pushnew ':coalton-release *features*))

(declaim (type boolean *coalton-disable-specialization*))
(defvar *coalton-disable-specialization*
  (cond
    ((find (uiop:getenv "COALTON_DISABLE_SPECIALIZATION")
           '("t" "true" "1")
           :test #'string-equal)
     t)
    (t
     ;; NOT because this variable disables specializations.
     (not (config ':perform-specialization :default t)))))

(declaim (type boolean *coalton-heuristic-inlining*))
(defvar *coalton-heuristic-inlining*
  (cond
    ((find (uiop:getenv "COALTON_HEURISTIC_INLINING")
           '("t" "true" "1")
           :test #'string-equal)
     t)
    (t
     (config ':perform-inlining :default nil))))

(declaim (type boolean *emit-type-annotations*))
(defvar *emit-type-annotations* (config ':emit-type-annotations :default t)
  "Configure the backend to insert or remove type annotations from the generated code.")

(declaim (type boolean *compile-print-types*))
(defvar *compile-print-types* (config ':print-types :default nil)
  "Print types of definitions to standard output on compile.")

(declaim (type boolean *print-specialization-occurrences*))
(defvar *print-specialization-occurrences* (config ':print-rewrites :default nil)
  "Print out information when a specialization occurs.")

(declaim (type boolean *print-inlining-occurrences*))
(defvar *print-inlining-occurrences* (config ':print-rewrites :default nil)
  "Print out information when an inline occurs.")

(declaim (type boolean *print-optimization-passes*))
(defvar *print-optimization-passes* (config ':print-rewrites :default nil)
  "Indicate when a node optimization pass is being performed.")

(declaim (type boolean *auto-continue-redefinition*))
(defvar *auto-continue-redefinition* (config ':auto-continue-redefinition :default nil)
  "When non-NIL, automatically continue with incompatible redefinitions instead of raising an error.
A warning will still be issued showing the affected functions.")

(defvar *coalton-optimize* '(optimize (speed 3) (safety 0)))

(defvar *coalton-optimize-library* '(optimize (speed 3) (safety 1)))


;;; Print (some of) the configuration

(format t "~&;; COALTON starting in ~:[development~;release~] mode~%" (coalton-release-p))
(format t "~&;; COALTON starting with specializations ~:[enabled~;disabled~]~%" *coalton-disable-specialization*)
(format t "~&;; COALTON starting with heuristic inlining ~:[disabled~;enabled~]~%" *coalton-heuristic-inlining*)
(format t "~&;; COALTON will~:[ not~;~] emit type annotations~%" *emit-type-annotations*)
